package com.yf.exam.modules.paper.service.impl;

import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yf.base.api.api.ApiError;
import com.yf.base.api.api.dto.PagingReqDTO;
import com.yf.base.api.exception.ServiceException;
import com.yf.base.enums.OpenType;
import com.yf.base.utils.*;
import com.yf.book.job.AddBookJob;
import com.yf.book.job.dto.AddBookDTO;
import com.yf.book.job.enums.BookType;
import com.yf.exam.enums.ExamState;
import com.yf.exam.enums.PaperExState;
import com.yf.exam.enums.PaperHandState;
import com.yf.exam.enums.PaperReviewState;
import com.yf.exam.job.BreakExamJob;
import com.yf.exam.job.ClearApplyJob;
import com.yf.exam.modules.exam.dto.ExamApplyDTO;
import com.yf.exam.modules.exam.dto.ExamDTO;
import com.yf.exam.modules.exam.dto.response.ExamCheckRespDTO;
import com.yf.exam.modules.exam.entity.Exam;
import com.yf.exam.modules.exam.service.ExamDepartService;
import com.yf.exam.modules.exam.service.ExamPersonService;
import com.yf.exam.modules.exam.service.ExamRecordService;
import com.yf.exam.modules.exam.service.ExamService;
import com.yf.exam.modules.paper.dto.PaperDTO;
import com.yf.exam.modules.paper.dto.ext.PaperGroupExtDTO;
import com.yf.exam.modules.paper.dto.ext.PaperQuAnswerExtDTO;
import com.yf.exam.modules.paper.dto.ext.PaperQuDetailDTO;
import com.yf.exam.modules.paper.dto.request.PaperListReqDTO;
import com.yf.exam.modules.paper.dto.response.ExamDetailRespDTO;
import com.yf.exam.modules.paper.dto.response.ExamResultRespDTO;
import com.yf.exam.modules.paper.dto.response.LeaveCheckRespDTO;
import com.yf.exam.modules.paper.dto.response.PaperListRespDTO;
import com.yf.exam.modules.paper.entity.Paper;
import com.yf.exam.modules.paper.entity.PaperGroup;
import com.yf.exam.modules.paper.entity.PaperQu;
import com.yf.exam.modules.paper.entity.PaperQuAnswer;
import com.yf.exam.modules.paper.mapper.PaperMapper;
import com.yf.exam.modules.paper.service.*;
import com.yf.exam.modules.tmpl.service.TmplService;
import com.yf.job.enums.JobGroup;
import com.yf.job.enums.JobPrefix;
import com.yf.job.service.JobService;
import com.yf.repo.enums.QuType;
import com.yf.system.modules.dict.service.SysDicValueService;
import com.yf.system.modules.user.UserUtils;
import com.yf.system.utils.ListUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.text.MessageFormat;
import java.util.*;

/**
 * <p>
 * 试卷业务实现类
 * </p>
 *
 * @author 聪明笨狗
 * @since 2020-05-25 16:33
 */
@Service
public class PaperServiceImpl extends ServiceImpl<PaperMapper, Paper> implements PaperService {


    @Autowired
    private ExamService examService;

    @Autowired
    private PaperService paperService;

    @Autowired
    private PaperQuService paperQuService;

    @Autowired
    private PaperQuAnswerService paperQuAnswerService;

    @Autowired
    private ExamRecordService examRecordService;

    @Autowired
    private TmplService tmplService;

    @Autowired
    private PaperGroupService paperGroupService;

    @Autowired
    private ExamPersonService examPersonService;

    @Autowired
    private ExamDepartService examDepartService;

    @Autowired
    private PaperCaptureService paperCaptureService;

    @Autowired
    private JobService jobService;

    @Autowired
    private SysDicValueService sysDicValueService;


    @Transactional(rollbackFor = Exception.class)
    @Override
    public String createPaper(String userId, String departCode, String examId, String password, String faceToken, String courseId) {

        // 校验是否有正在考试的试卷
        QueryWrapper<Paper> wrapper = new QueryWrapper<>();
        wrapper.lambda()
                .eq(Paper::getUserId, userId)
                .eq(Paper::getHandState, PaperHandState.ING);
        Paper pp = this.getOne(wrapper, false);
        if (pp!=null) {
            throw new ServiceException(ApiError.ERROR_20010002);
        }

        ExamCheckRespDTO check = examService.findCheckInfo(userId, examId);

        // 人员开放的
        if(OpenType.PERSON.equals(check.getOpenType())){
            boolean in = examPersonService.inList(examId, userId);
            if(!in){
                throw new ServiceException("您不在参考名单里面，请确认！");
            }
        }

        // 部门开放的
        if(OpenType.DEPART.equals(check.getOpenType())){
            boolean in = examDepartService.inList(examId, departCode);
            if(!in){
                throw new ServiceException("您不在参考部门里面，请确认！");
            }
        }

        // 私密考试，需要检测是否学完
        if(OpenType.HIDDEN.equals(check.getOpenType())){
            throw new ServiceException("请通过学习来解锁此考试！");
        }

        // 未开始或已结束
        if(check.getOverTime()!=null && check.getOverTime()){
            throw new ServiceException("考试未开始或已结束！");
        }

        // 迟到了
        if(check.getLate()!=null && check.getLate()){
            throw new ServiceException(MessageFormat.format("您已经迟到超过{0}分钟，不允许进入考试！", check.getLateMax()));
        }

        // 机会超限了
        if(check.getChance()!=null
                && check.getChance() > 0
                && check.getExamCount()!=null
                && check.getExamCount()>= check.getChance()){

            throw new ServiceException(MessageFormat.format("考试次数超限，总共有{0}次考试机会！", check.getChance()));
        }

        if (check == null) {
            throw new ServiceException("考试不存在！");
        }

        if (!ExamState.ENABLE.equals(check.getState())) {
            throw new ServiceException("考试状态不正确！");
        }


        if (OpenType.NEED_PASS.equals(check.getOpenType())) {

            // 空密码，处理特别申请
            if (StringUtils.isBlank(password)) {
                password = "";
            }

            // 需要密码的
            if(check.getIgnorePass()==null || !check.getIgnorePass()){
                if (!password.equals(check.getPassword())) {
                    throw new ServiceException("考试密码不正确！");
                }
            }
        }


        // 校验人脸是否通过
        if (check.getFaceOn() != null && check.getFaceOn()) {
            paperCaptureService.checkFace(examId, userId);
        }

        // 从试卷中提取题目表
        List<PaperGroupExtDTO> quList = tmplService.findForCreate(check.getTmplId());

        if (CollectionUtils.isEmpty(quList)) {
            throw new ServiceException("规则不正确，无对应的考题！");
        }

        //保存试卷内容
        Paper paper = this.savePaper(userId, check, quList);
        String paperId = paper.getId();

        // 增加用户考试次数
        examRecordService.increaseTryCount(userId, check.getId());

        // 使用机会
        ExamApplyDTO applyDTO = new ExamApplyDTO();
        applyDTO.setApplyUser(userId);
        applyDTO.setExamId(examId);
        jobService.addCronJob(ClearApplyJob.class, null, JSON.toJSONString(applyDTO));

        // 创建超时交卷任务
        String jobName = JobPrefix.BREAK_EXAM + paperId;
        jobService.addCronJob(BreakExamJob.class, jobName, CronUtils.dateToCron(paper.getLimitTime()), paperId);

        // 异步将已经考完的试卷修改为复写状态
        this.markOverwrite(userId, examId, paperId);

        return paperId;
    }

    @Override
    public ExamDetailRespDTO paperDetail(String paperId) {

        ExamDetailRespDTO respDTO = new ExamDetailRespDTO();

        // 题目基本信息
        Paper paper = paperService.getById(paperId);
        if (paper == null) {
            throw new ServiceException("试卷不存在或已被删除，无法继续考试！");
        }

        // 带入截屏频率
        ExamDTO exam = examService.findById(paper.getExamId());
        if (exam == null) {
            throw new ServiceException("考试不存在或已被删除，无法继续考试！");
        }

        // 先复制考试
        BeanMapper.copy(exam, respDTO);

        // 再复制试卷信息
        BeanMapper.copy(paper, respDTO);

        // 填充数据
        List<PaperGroupExtDTO> groupList = paperGroupService.listCardGroup(paperId);
        respDTO.setGroupList(groupList);

        return respDTO;
    }


    /**
     * 阅卷检索试卷
     * @param paperId
     * @return
     */
    @Override
    public ExamResultRespDTO paperResult(String paperId) {

        ExamResultRespDTO respDTO = new ExamResultRespDTO();

        // 题目基本信息
        Paper paper = paperService.getById(paperId);
        if(paper == null){
            throw new ServiceException("啊哦，试卷走丢了！");
        }
        BeanMapper.copy(paper, respDTO);

        // 考试基本信息
        ExamDTO exam = examService.findById(paper.getExamId());
        respDTO.setResultType(exam.getResultType());
        respDTO.setThanks(exam.getThanks());

        // 完整显示时才查找题目结果
        List<PaperGroupExtDTO> groupList = paperGroupService.listGroup(paperId);
        respDTO.setGroupList(groupList);

        return respDTO;
    }

    @Override
    public PaperQuDetailDTO findQuDetail(String paperId, String quId) {

        // 题目不存在了，不能考试
        PaperQuDetailDTO respDTO = paperQuService.findPaperQu(paperId, quId);

        if (respDTO == null) {
            // 无法考试 TODO 标记
            throw new ServiceException(ApiError.ERROR_20010001);
        }
        return respDTO;
    }


    /**
     * 修改为已覆盖
     * @param userId
     * @param examId
     */
    private void markOverwrite(String userId, String examId, String paperId){
        QueryWrapper<Paper> wrapper = new QueryWrapper<>();
        wrapper.lambda()
                .select(Paper::getId)
                .eq(Paper::getExamId, examId)
                .ne(Paper::getId, paperId)
                .ne(Paper::getOverwrite, true)
                .eq(Paper::getUserId, userId);

        // 根据ID修改，避免全表扫描
        List<Paper> papers = this.list(wrapper);
        if(CollectionUtils.isEmpty(papers)){
            return;
        }

        for(Paper item: papers){
            item.setOverwrite(true);
            this.updateById(item);
        }
    }

    /**
     * 保存试卷
     *
     * @param userId
     * @param exam
     * @param groupList
     * @return
     */
    private Paper savePaper(String userId, ExamCheckRespDTO exam, List<PaperGroupExtDTO> groupList) {


        //保存试卷基本信息
        Paper paper = new Paper();

        // 复制基本一致的数据
        BeanMapper.copy(exam, paper);
        // 个性数据
        paper.setId(IdWorker.getIdStr()+ RandomStringUtils.randomAlphanumeric(10));
        paper.setDeptCode(UserUtils.departCode());
        paper.setExamId(exam.getId());
        paper.setUserScore(DecimalUtils.zero());
        paper.setSubjScore(DecimalUtils.zero());
        paper.setObjScore(DecimalUtils.zero());
        paper.setUserId(userId);
        paper.setPassed(false);
        paper.setHandState(PaperHandState.ING);
        paper.setReviewState(PaperReviewState.WAIT);


        // 截止时间
        Calendar cl = Calendar.getInstance();
        cl.setTimeInMillis(System.currentTimeMillis());
        cl.add(Calendar.MINUTE, exam.getTotalTime());

        // 以实际结束时间为准
        Date finishTime = cl.getTime();

        if(finishTime.after(exam.getEndTime())){
            finishTime = exam.getEndTime();
        }

        // 保存题目
        if (!CollectionUtils.isEmpty(groupList)) {
            this.savePaperQu(paper, groupList);
        }

        // 填充信息
        paper.setLimitTime(finishTime);
        paper.setReviewRequire(DecimalUtils.gt(paper.getSubjScore(), 0));
        paperService.save(paper);

        return paper;
    }


    /**
     * 批量一次性保存全部题目的数据库中
     *
     * @param paper
     * @param groupList
     */
    private void savePaperQu(Paper paper, List<PaperGroupExtDTO> groupList) {


        // 试卷ID
        String paperId = paper.getId();

        List<PaperGroup> paperGroupList = new ArrayList<>();
        List<PaperQu> paperQuList = new ArrayList<>();
        List<PaperQuAnswer> paperQuAnswerList = new ArrayList<>();

        // 引用排序
        int sort = 1;

        for (PaperGroupExtDTO group : groupList) {

            // 保存大题信息
            PaperGroup paperGroup = new PaperGroup();
            BeanMapper.copy(group, paperGroup);
            paperGroup.setId(IdWorker.getIdStr());
            paperGroup.setPaperId(paperId);
            paperGroupList.add(paperGroup);

            // 预处理大题和小题
            List<PaperQuDetailDTO> quList = group.getQuList();

            // 处理题目并返回排序
            sort = this.processQuList(sort, paperQuList, paperQuAnswerList, paper, paperGroup.getId(), group.getPerScore(), quList, "");
        }

        // 保存全部
        paperGroupService.saveBatch(paperGroupList);

        //添加问题
        paperQuService.saveBatch(paperQuList);

        //批量添加问题答案
        paperQuAnswerService.saveBatch(paperQuAnswerList);
    }


    /**
     * 预处理题目列表、区分大题和子题目
     *
     * @param paperQuList
     * @param paperQuAnswerList
     * @param paper
     * @param groupId
     * @param perScore
     * @param quList
     * @param refId
     */
    private int processQuList(int sort,
                               List<PaperQu> paperQuList,
                               List<PaperQuAnswer> paperQuAnswerList,
                               Paper paper,
                               String groupId,
                               BigDecimal perScore,
                               List<PaperQuDetailDTO> quList,
                               String refId) {

        for (int i = 0; i < quList.size(); i++) {
            PaperQuDetailDTO qu = quList.get(i);
            PaperQu paperQu = new PaperQu();
            BeanMapper.copy(qu, paperQu);
            paperQu.setPaperId(paper.getId());
            paperQu.setGroupId(groupId);
            paperQu.setSort(sort);
            paperQu.setId(IdWorker.getIdStr());

            // 子题目特有属性
            if (StringUtils.isNotBlank(refId)) {
                paperQu.setChild(true);
                paperQu.setRefId(refId);
            }

            // 如果未独立设置分值，则为全局的，一般为随机组卷
            if (paperQu.getScore() == null || DecimalUtils.isZero(paperQu.getScore())) {
                paperQu.setScore(perScore);
            }

            // 重新累计整卷的分数，SubjScore、ObjScore为整卷的分数，不是用户得分
            if (!QuType.MIX.equals(paperQu.getQuType())) {
                if (QuType.SAQ.equals(paperQu.getQuType())) {
                    paper.setSubjScore(DecimalUtils.add(paper.getSubjScore(), paperQu.getScore()));
                } else {
                    paper.setObjScore(DecimalUtils.add(paper.getObjScore(), paperQu.getScore()));
                }
            }


            paperQu.setAnswered(false);
            paperQu.setActualScore(DecimalUtils.zero());
            paperQuList.add(paperQu);

            List<PaperQuAnswerExtDTO> answerList = qu.getAnswerList();
            if (!CollectionUtils.isEmpty(answerList)) {

                int j = 0;
                for (PaperQuAnswerExtDTO answer : answerList) {
                    PaperQuAnswer paperQuAnswer = new PaperQuAnswer();
                    BeanMapper.copy(answer, paperQuAnswer);
                    paperQuAnswer.setId(IdWorker.getIdStr());
                    paperQuAnswer.setPaperId(paper.getId());
                    paperQuAnswer.setQuId(qu.getQuId());
                    paperQuAnswer.setAnswerId(answer.getAnswerId());
                    paperQuAnswer.setChecked(false);
                    paperQuAnswer.setSort(j);
                    paperQuAnswer.setGroupId(answer.getGroupId());
                    paperQuAnswer.setAbc(AbcTags.get(j));
                    paperQuAnswer.setIsRight(answer.getIsRight());
                    j++;

                    paperQuAnswerList.add(paperQuAnswer);
                }
            }

            sort++;

            // 子项目列表
            List<PaperQuDetailDTO> subList = qu.getSubList();
            if (CollectionUtils.isEmpty(subList)) {
                continue;
            }

            // 子列表被平均的分数，并填充到各个题目中
            List<BigDecimal> scoreList = CalcUtils.avgSplit(perScore, subList.size());
            for(int j=0; j<subList.size(); j++){
                PaperQuDetailDTO sub = subList.get(j);
                if(sub.getScore() == null || DecimalUtils.isZero(sub.getScore())){
                    sub.setScore(scoreList.get(j));
                }
            }

            // 再次构建子题目
            this.processQuList(1, paperQuList, paperQuAnswerList, paper, groupId, perScore, subList, paperQu.getId());
        }

        return sort;
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean fillAnswer(PaperQuDetailDTO reqDTO) {

        // 校验试卷状态
        this.checkForAnswer(reqDTO.getPaperId());

        String quType = reqDTO.getQuType();

        // 修改为已回答
        PaperQu qu = paperQuService.findByKey(reqDTO.getPaperId(), reqDTO.getQuId());

        // 单选多选判断，本质是一样的处理
        if (QuType.RADIO.equals(quType) || QuType.MULTI.equals(quType) || QuType.JUDGE.equals(quType)) {
            // 保存和校验
            this.checkAndSave13(reqDTO, qu);
        }

        // 填空题
        if (QuType.FILL.equals(quType)) {
            // 校验
            this.checkAndSave5(reqDTO, qu, qu.getAnswerOrder());
        }

        // 简答题
        if (QuType.SAQ.equals(quType)) {
            if(!StringUtils.isBlank(reqDTO.getAnswer()) || !StringUtils.isBlank(reqDTO.getMedia())){
                qu.setAnswered(true);
                qu.setAnswer(reqDTO.getAnswer());
                qu.setMedia(reqDTO.getMedia());
            }else{
                qu.setAnswered(false);
            }
        }


        // 已答标识
        boolean answered = qu.getAnswered();


        // 组合题
        if (QuType.MIX.equals(quType)) {

            boolean allRight = true;

            List<PaperQuDetailDTO> subList = reqDTO.getSubList();
            if (!CollectionUtils.isEmpty(subList)) {
                for (PaperQuDetailDTO sub : subList) {
                    // 填充答案
                    boolean effect = this.fillAnswer(sub);

                    // 一个已答就算
                    if (effect) {
                        answered = true;
                    }

                    // 一个错就当错
                    if(!sub.getIsRight()){
                        allRight = false;
                    }
                }
            }

            qu.setIsRight(allRight);
        }

        // 不改变数据
        qu.setMark(reqDTO.getMark());
        qu.setAnswered(answered);

        paperQuService.updateByKey(qu);
        return answered;
    }


    /**
     * 校验和保存单选、多选、判断题
     *
     * @param reqDTO
     * @return
     */
    private void checkAndSave13(PaperQuDetailDTO reqDTO, PaperQu qu) {

        // 用户的回答
        List<PaperQuAnswerExtDTO> answers = reqDTO.getAnswerList();


        // 完整的答案，包括正确错误的
        List<PaperQuAnswerExtDTO> fullList = paperQuAnswerService.listForExam(qu.getPaperId(), qu.getQuId());

        // 正确的参考答案，只有正确的
        List<String> rightList = this.rightList(fullList, false);

        // 用户答案
        List<String> userList = new ArrayList<>();

        // 错误也给分
        BigDecimal pathScore = DecimalUtils.zero();

        // 循环标准答案
        boolean answered = false;

        for (PaperQuAnswerExtDTO an : answers) {
            if (an.getChecked()) {
                userList.add(an.getAnswerId());
                answered = true;
            }
        }

        // 漏选也给分：错选不给分
        if(rightList.size() >= userList.size()){
            boolean pathCalc = true;
            // 只要错选一题就不给分
            for(String item: userList){
                if(!rightList.contains(item)){
                    pathCalc = false;
                    break;
                }
            }

            // 正常给分
            if(pathCalc){
                for (PaperQuAnswerExtDTO item : fullList) {
                    if (DecimalUtils.gt(item.getPathScore(), 0) && userList.contains(item.getAnswerId())) {
                        pathScore = DecimalUtils.add(pathScore, item.getPathScore());
                    }
                }
            }
        }


        // 保存用户答案
        List<PaperQuAnswer> list = BeanMapper.mapList(answers, PaperQuAnswer.class);
        paperQuAnswerService.updateBatchById(list);

        // 判断是否正确
        boolean right = ListUtils.compareList(false, rightList, userList);

        // 正确全给分，错误给部分
        qu.setAnswered(answered);
        qu.setIsRight(right);
        qu.setActualScore(right ? qu.getScore() : pathScore);

        // 回写内部使用
        reqDTO.setIsRight(right);
    }

    /**
     * 校验和保存填空题
     *
     * @param reqDTO
     * @return
     */
    private void checkAndSave5(PaperQuDetailDTO reqDTO, PaperQu qu, boolean sort) {

        // 用户的回答
        List<PaperQuAnswerExtDTO> answers = reqDTO.getAnswerList();

        // 完整的答案，包括正确错误的
        List<PaperQuAnswerExtDTO> fullList = paperQuAnswerService.listForExam(qu.getPaperId(), qu.getQuId());

        // 正确的参考答案，只有正确的
        List<String> rightList = this.rightList(fullList, true);

        // 用户答案
        List<String> userList = new ArrayList<>();

        boolean answered = false;

        // 循环标准答案
        for (PaperQuAnswerExtDTO an : answers) {
            if (!StringUtils.isBlank(an.getAnswer())) {
                userList.add(an.getAnswer());
                answered = true;
            }
        }

        // 保存用户答案
        List<PaperQuAnswer> list = BeanMapper.mapList(answers, PaperQuAnswer.class);
        paperQuAnswerService.updateBatchById(list);

        // 判断是否正确
        boolean right = ListUtils.compareList(sort, rightList, userList);
        qu.setIsRight(right);
        qu.setAnswered(answered);
        qu.setActualScore(right ? qu.getScore() : new BigDecimal(0));
        // 回写内部使用
        reqDTO.setIsRight(right);
    }


    /**
     * 提取出正确的列表
     *
     * @param fullList
     * @param fill
     * @return
     */
    private List<String> rightList(List<PaperQuAnswerExtDTO> fullList, boolean fill) {

        // 正确项
        List<String> rightMap = new ArrayList<>();

        for (PaperQuAnswerExtDTO item : fullList) {
            if (fill) {
                rightMap.add(item.getContent());
                continue;
            }
            if (item.getIsRight()) {
                rightMap.add(item.getAnswerId());
            }
        }
        return rightMap;
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public void handExam(String paperId, Integer exState, String message) {

        //获取试卷信息
        Paper paper = paperService.getById(paperId);

        if(paper == null){
            return;
        }

        // 说明已经交卷了或中断了，不操作
        if (!PaperHandState.ING.equals(paper.getHandState())) {
            return;
        }

        // 考试信息
        Exam exam = examService.getById(paper.getExamId());

        //如果不是正常的，抛出异常
        if (!PaperHandState.ING.equals(paper.getHandState())) {
            throw new ServiceException("试卷状态不正确！");
        }

        // 正常交卷判断
        if (PaperExState.NORMAL.equals(exState)) {
            // 最短交卷时间限制
            if (exam.getHandMin() != null && exam.getHandMin() > 0) {

                Calendar cl = Calendar.getInstance();
                cl.setTime(paper.getCreateTime());
                cl.add(Calendar.MINUTE, exam.getHandMin());

                if (System.currentTimeMillis() < cl.getTimeInMillis()) {
                    throw new ServiceException(MessageFormat.format("考试开始后{0}分钟后才能交卷！", exam.getHandMin()));
                }
            }
        }

        // 客观分
        BigDecimal objScore = paperQuService.sumObjective(paperId);
        paper.setUserObjScore(objScore);
        paper.setUserScore(objScore);


        // 主观分，因为要阅卷，所以给0
        paper.setUserSubjScore(DecimalUtils.zero());

        // 是否通过
        boolean passed = DecimalUtils.ge(objScore, paper.getQualifyScore());
        paper.setPassed(passed);

        // 待阅卷
        if (paper.getReviewRequire()) {
            paper.setReviewState(PaperReviewState.WAIT);
            // 同步用户考试状态
            examRecordService.joinResult(false, paper.getUserId(), paper.getExamId(), DecimalUtils.zero(), passed);
        } else {
            // 同步保存考试成绩
            examRecordService.joinResult(true, paper.getUserId(), paper.getExamId(), objScore, passed);
            paper.setReviewState(PaperReviewState.FINISHED);
        }

        paper.setUpdateTime(new Date());
        //计算考试时长
        Calendar cl = Calendar.getInstance();
        cl.setTimeInMillis(System.currentTimeMillis());
        int userTime = (int) ((System.currentTimeMillis() - paper.getCreateTime().getTime()) / 1000 / 60);
        if (userTime == 0) {
            userTime = 1;
        }

        paper.setHandTime(new Date());
        paper.setHandState(PaperHandState.HANDED);
        paper.setExMsg(message);
        paper.setExState(exState);
        paper.setUserTime(userTime);

        //更新试卷
        paperService.updateById(paper);

        // 终止定时任务
        String name = JobPrefix.BREAK_EXAM + paperId;
        jobService.deleteJob(name, JobGroup.SYSTEM);

        // 加入错题任务
        new Thread(() -> {
            AddBookDTO dto = new AddBookDTO();
            dto.setVisible(exam.getShowBook());
            dto.setUserId(paper.getUserId());
            dto.setRefType(BookType.EXAM);
            dto.setRefId(paper.getExamId());
            List<String> ids = paperQuService.listWrongIds(paperId);
            dto.setIds(ids);
            jobService.addCronJob(AddBookJob.class, null, JSON.toJSONString(dto));
        }).start();

        //设置主观分
        PaperServicei(paperId);
    }

    @Override
    public void PaperServicei(String paperId) {
        ExamResultRespDTO examResultRespDTO = this.paperResult(paperId);
        SubjectiveScores(examResultRespDTO);
    }

    /**
     * 封装考试参数
     * @param examResultRespDTO
     * @return
     */
    private BigDecimal SubjectiveScores( ExamResultRespDTO examResultRespDTO) {
//        ExamResultRespDTO examResultRespDTO = this.paperResult(paperId);
//        if (examResultRespDTO.getReviewState()==1){
//            return null;
//        }
        //获取试题
        List<PaperGroupExtDTO> groupList = examResultRespDTO.getGroupList();
        List<JSONObject> quList=new ArrayList<>();
        for (PaperGroupExtDTO group : groupList){
            //判断试题类型
            if (group.getQuType().equals("5")){
                //填空题
                for (PaperQuDetailDTO item:group.getQuList()){
                    //小题分数
                    BigDecimal score = item.getScore();
                    for (PaperQuAnswerExtDTO answer : item.getAnswerList()){
                        //定义解析集合
                        JSONObject jsonObject=new JSONObject();
                        //标准答案
                        String count=answer.getContent();
                        //用户回答
                        String useranswer=answer.getAnswer();
                        //试题id
                        String quId=answer.getQuId();
                        jsonObject.put("question_id",quId);
                        jsonObject.put("answer",count.equals("") ? "无" : count);
                        jsonObject.put("student_answer",useranswer.equals("") ? "无" : useranswer);
                        jsonObject.put("score",score);
                        quList.add(jsonObject);
                    }
                }
            } else if (group.getQuType().equals("4")) {
                //简答题
                for (PaperQuDetailDTO item:group.getQuList()){
                    JSONObject jsonObject=new JSONObject();
                    //小题分数
                    BigDecimal score = item.getScore();
                    //标准答案
                    String analysis=item.getAnalysis();
                    //用户回答
                    String useranswer=item.getAnswer();
                    jsonObject.put("question_id",item.getQuId());
                    jsonObject.put("answer",analysis.equals("") ? "无" : analysis);
                    jsonObject.put("student_answer",useranswer.equals("") ? "无" : useranswer);
                    jsonObject.put("score",score);
                    quList.add(jsonObject);
                }
            }
        }
        //将参数送入系统中
        JSONObject jsonObject=new JSONObject();
        jsonObject.put("question_list",quList);
        getScore(jsonObject,examResultRespDTO);
        return null;
    }

    private int paperType=0;
    private List<Map<String,Object>> getScore(JSONObject jsonObject,ExamResultRespDTO examResultRespDTO){
        List<PaperGroupExtDTO> groupList = examResultRespDTO.getGroupList();
        int tKscores=0;
        int jDscore=0;
        Map<String, String> aiExamConf = sysDicValueService.findDictMap("aiExam_conf");
        //获取知识点
        String host = aiExamConf.get("pathUrl");
        String api=aiExamConf.get("scoreApiPath");
//        HttpRequest request = HttpRequest.post(host+"/scoreapi/"+"/subjective_score");
        HttpRequest request = HttpRequest.post(host+api);
        Map<String,String> header=new HashMap<>();
        header.put("Content-Type","application/json");
        header.put("Connection","keep-alive");
        header.put("Accept","text/html;charset=UTF-8;");
        header.put("Access-Control-Allow-Origin", "*");
        request.addHeaders(header);
        request.body(jsonObject.toJSONString());
        request.timeout(800000);
        // 发送请求并获取HttpResponse对象
        HttpResponse response=null;
        try {
            response= request.execute();
        }catch (Exception e){
            System.out.println("*****************************************************************************************");
            System.out.println(e.fillInStackTrace());
            System.out.println(e.toString());
        }

        if (response.getStatus()==200){
            String body = response.body();
            JSONObject jsonObject1=JSONObject.parseObject(body);
            if (body!=null){
                //获取数组
                JSONArray jsonArray=jsonObject1.getJSONArray("data");
                for (PaperGroupExtDTO group : groupList){
                    //判断试题类型
                    if (group.getQuType().equals("5")){
                        //填空题
                        for (PaperQuDetailDTO item:group.getQuList()){
                            for (PaperQuAnswerExtDTO answer : item.getAnswerList()){
                                //试题id
                                String quId=answer.getQuId();
                                for (int i = 0; i < jsonArray.size(); i++){
                                    JSONObject jsonObject2=jsonArray.getJSONObject(i);
                                    if (jsonObject2.get("question_id").equals(quId)){
                                        if (!jsonObject2.get("student_score").equals(0)) {
                                            answer.setIsRight(true);
                                            answer.setPathScore(new BigDecimal(jsonObject2.getInteger("student_score")));
                                            item.setIsRight(true);
                                            item.setActualScore(new BigDecimal(jsonObject2.getInteger("student_score")));
                                            tKscores=tKscores+jsonObject2.getInteger("student_score");
                                        }
                                    }
                                }

                            }
                        }
                    } else if (group.getQuType().equals("4")) {
                        //简答题
                        for (PaperQuDetailDTO item:group.getQuList()){
                            String quId = item.getQuId();
                            for (int i = 0; i < jsonArray.size(); i++){
                                JSONObject jsonObject2=jsonArray.getJSONObject(i);
                                if (jsonObject2.get("question_id").equals(quId)){
                                    if (!jsonObject2.get("student_score").equals(0)) {
                                        item.setIsRight(true);
                                        item.setActualScore(new BigDecimal(jsonObject2.getInteger("student_score")));
                                        jDscore=jDscore+jsonObject2.getInteger("student_score");
                                    }
                                }
                            }
                        }
                    }
                }
                //主观分相加
                tKscores=tKscores+jDscore;
                //开始分数
                BigDecimal totalScore = examResultRespDTO.getTotalScore();
                totalScore=totalScore.multiply(new BigDecimal(tKscores));
                //获取客观分
//                BigDecimal objScore = examResultRespDTO.getObjScore();
                //设置主观分
                examResultRespDTO.setTotalScore(totalScore);
                examResultRespDTO.setResultType(1);
                examResultRespDTO.setIsAutoCheck(1);
                this.reviewPaper(examResultRespDTO);
            }
        }else {
            System.out.println("*****************************************************************************************");
            System.out.println(response.body());
            System.out.println(response.getStatus());
        }
        return null;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void reviewPaper(ExamResultRespDTO reqDTO) {


        List<PaperGroupExtDTO> groupList = reqDTO.getGroupList();

        List<PaperQu> list = new ArrayList<>();
        for (PaperGroupExtDTO group : groupList) {

            List<PaperQuDetailDTO> quList = group.getQuList();

            for (PaperQuDetailDTO item : quList) {
                PaperQu qu = new PaperQu();
                qu.setId(item.getId());
                qu.setActualScore(item.getActualScore());
                qu.setIsRight(item.getIsRight());
                list.add(qu);

                // 组合题要循环子题目
                if (QuType.MIX.equals(item.getQuType())) {
                    for (PaperQuDetailDTO item1 : item.getSubList()) {
                        PaperQu sub = new PaperQu();
                        sub.setId(item1.getId());
                        sub.setActualScore(item1.getActualScore());
                        sub.setIsRight(item1.getIsRight());
                        list.add(sub);
                    }
                }

            }

        }

        // 批量修改
        paperQuService.updateBatchById(list);

        // 主观题
        BigDecimal subjScore = paperQuService.sumSubjective(reqDTO.getId());
        // 客观题
        BigDecimal objScore = paperQuService.sumObjective(reqDTO.getId());
        // 总分
        BigDecimal userScore = DecimalUtils.add(subjScore, objScore);

        // 修改试卷状态
        Paper paper = this.getById(reqDTO.getId());
        paper.setUserSubjScore(subjScore);
        paper.setUserObjScore(objScore);
        paper.setUserScore(userScore);
        if (reqDTO.getIsAutoCheck()!=null && reqDTO.getIsAutoCheck()==1) {
            paper.setReviewState(PaperReviewState.WAIT);
        }else {
            paper.setReviewState(PaperReviewState.FINISHED);
        }
        paper.setUpdateTime(new Date());
        paper.setReviewUser(UserUtils.getUserId());
        paper.setReviewMsg(reqDTO.getReviewMsg());
        boolean passed = DecimalUtils.ge(paper.getUserScore(), paper.getQualifyScore());
        paper.setPassed(passed);
        this.updateById(paper);

        // 同步保存考试成绩
        examRecordService.joinResult(true, paper.getUserId(), paper.getExamId(), paper.getUserScore(), passed);
    }

    @Override
    public IPage<PaperListRespDTO> paging(PagingReqDTO<PaperListReqDTO> reqDTO) {
        return baseMapper.paging(reqDTO.toPage(), reqDTO.getParams());
    }

    @Override
    public IPage<PaperListRespDTO> myPaging(PagingReqDTO<PaperListReqDTO> reqDTO) {
        return baseMapper.myPaging(reqDTO.toPage(), reqDTO.getParams());
    }


    @Override
    public PaperDTO checkProcess(String userId) {

        QueryWrapper<Paper> wrapper = new QueryWrapper<>();
        wrapper.lambda()
                .eq(Paper::getUserId, userId)
                .eq(Paper::getHandState, PaperHandState.ING);

        Paper paper = this.getOne(wrapper, false);

        if (paper != null) {
            return BeanMapper.map(paper, PaperDTO.class);
        }

        return null;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public LeaveCheckRespDTO leave(String id) {

        Paper paper = this.getById(id);

        if(paper == null){
            throw new ServiceException("考卷不存在，请确认参数正确！");
        }

        // 增加离开次数
        if (paper.getLeaveTimes() == null || paper.getLeaveTimes() == 0) {
            paper.setLeaveTimes(1);
        } else {
            paper.setLeaveTimes(paper.getLeaveTimes() + 1);
        }

        // 更新考卷
        this.updateById(paper);

        // 考试
        Exam exam = examService.getById(paper.getExamId());

        boolean overTimes = false;

        // 超限了，强制交卷
        if(exam.getLeaveOn()!=null
                && exam.getLeaveOn()
                && exam.getLeaveCount()>0
                && paper.getLeaveTimes()>= exam.getLeaveCount()){
            this.handExam(paper.getId(), PaperExState.LEAVE_OVERTIME, "离开次数过多强制交卷");
            overTimes = true;
        }

        LeaveCheckRespDTO respDTO = new LeaveCheckRespDTO();
        respDTO.setOverLimit(overTimes);
        respDTO.setLeaveTimes(paper.getLeaveTimes());
        respDTO.setLeaveCount(exam.getLeaveCount());
        return respDTO;
    }

    @Override
    public int countProcess(String examId) {

        QueryWrapper<Paper> wrapper = new QueryWrapper<>();
        wrapper.lambda()
                .eq(Paper::getExamId, examId)
                .eq(Paper::getHandState, PaperHandState.ING);

        return this.count(wrapper);
    }

    @Override
    public int countByTmpl(String tmplId) {
        return baseMapper.countByTmpl(tmplId);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void handAll(String examId, Integer exState, String message) {

        QueryWrapper<Paper> wrapper = new QueryWrapper<>();
        wrapper.lambda()
                .select(Paper::getId)
                .eq(Paper::getExamId, examId)
                .eq(Paper::getHandState, PaperHandState.ING);

        List<Paper> list = this.list(wrapper);

        if(CollectionUtils.isEmpty(list)){
            return;
        }

        for(Paper paper: list){
            this.handExam(paper.getId(), exState, message);
        }

    }

    @Override
    public void saveById(Paper paper) {
        baseMapper.updateById(paper);
    }




    /**
     * 校验试卷状态
     * @param paperId
     */
    public void checkForAnswer(String paperId) {

        QueryWrapper<Paper> wrapper = new QueryWrapper<>();
        wrapper.lambda()
                .select(Paper::getId, Paper::getHandState)
                .eq(Paper::getId, paperId);
        wrapper.last(" FOR UPDATE ");

        Paper paper = this.getOne(wrapper, false);
        if(paper == null){
            throw new ServiceException("试卷不存在或已被删除！");
        }

        if(PaperHandState.HANDED.equals(paper.getHandState())){
            throw new ServiceException("当前试卷已交卷，无法再继续作答，请点击`提交试卷`或直接关闭本页面！");
        }
    }

}
